package philbot;

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;

import de.tabacha.cgo.Board;
import de.tabacha.cgo.Constants;
import de.tabacha.cgo.Engine;
import de.tabacha.cgo.Field;
import de.tabacha.cgo.InvalidMoveException;
import de.tabacha.cgo.Jump;
import de.tabacha.cgo.Move;
import de.tabacha.cgo.Put;

/*
 * Hooks everything up to the ConwayGo UI and existing bots
 * this is included for historical reasons, and to keep the code base consistent
 * does not currently work.  DONOT try to use philbot in Graphical Mode
 */
public class PhilBotAdaptor implements Engine {

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return "PhilBot";
	}

	@Override
	public String getVersion() {
		// TODO Auto-generated method stub
		return "0.1";
	}

	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return "Stupid Random PhilBot";
	}

	@Override
	public String getAuthor() {
		// TODO Auto-generated method stub
		return "Philip Johnson-Freyd";
	}

	@Override
	public boolean canPlayBothSides() {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void initGame(long randSeed) {
		// TODO Auto-generated method stub
	}

	@Override
	public Move think(Board position) {
		int depth = 2;
		// TODO Initalize and configure negamax
		Move best = getFirstPut(position);
		Jump jump = getBestJump(position,depth);
		if(jump.size() == 0){
			 Move put = getBestPut(position, depth);
			 if(put != null){
				 best = put;
			 }
		}else{
			best = jump;
		}
		return best;
	}
	
	private Move getFirstPut(Board position){
		Move m = null;
		for(int i = 0; i < 19; i++){
			for(int j = 0; j< 19; j++){
				if(position.on(i, j) == Constants.EMPTY){
					m = new Put(new Field(i,j));
					break;
				}
			}
		}
		return m;
	}
	//TODO Make not broken
	/*
	 * Returns the strongest jump
	 */
	private Jump getBestJump(Board position, int depth){
		/*
		 * The algorithm here is pretty simple.  We just want to find the best possible jump.
		 * For the purposes of ConwayGo a jump is any serious of simple jumps
		 * The problem with this for me is that just generating all such jumps is NP hard!
		 * So instead we take a greedy approach where we pick the best simple jump at each position
		 * This will produce the correct answer IF we correctly call the negamax based search.
		 * Thats because the negamax does all the hard work of looking over the full state space
		 */
		boolean dontJump = false;
		byte d = bestJumpDirect(position, depth);
		if((d == 9)||dontJump){
			return new Jump();
		}else{
			LinkedList<Byte> l = new LinkedList<Byte>();
			boolean cond = true;
			while(cond){
				Board temp = (Board) position.clone();
				l.add(d);
				try {temp.move( directionsToJump(l,null) );} catch (InvalidMoveException e) {/*do nothing*/}
				if(depth > 2)
					depth = depth - 2;
				d = bestJumpDirect(temp, depth);
				if(d == 9)
					cond = false;
			}
			return directionsToJump(l, null);
		}
	}
	
	private byte bestJumpDirect(Board p, int depth){
		//end jump
		/*
		int bestVal = adjuster(depth)*PhilBotCore.negaMax(depth, new WrapperPosition(p, States.START, true), true);
		System.out.println("starting strength: " + bestVal);
		*/
		byte bestDirec = 9;
		/*
		for(byte i = 0; i < 8; i++){
			Board temp = (Board) p.clone();
			Jump trialJump = directionsToJump(new LinkedList<Byte>(),i);
			try{
				temp.move(trialJump);
				int trialStrength = adjuster(depth)*PhilBotCore.negaMax(depth, new WrapperPosition(temp, States.NULLMOVE,true), true);
				System.out.println("trial strength is at: " + trialStrength);
				//need to do another check
				if((trialStrength > bestVal)){
					bestVal = trialStrength;
					bestDirec = i;
					System.out.println("strength is at: " + bestVal);
				}
			}catch(InvalidMoveException e){
				//do nothing with invalid move, this will work because you can never be in a situation where no moves are valid
			}
		}
		if(bestDirec == 9)
			System.out.println("stop");
			*/
		return bestDirec;
	}
	/*
	 * Helper method for turning a list of directions into a "jump" move
	 */
	private Jump directionsToJump(LinkedList<Byte> lst, Byte next){
		int n = lst.size();
		if(next != null){
			n+= 1;
		}
		byte[] array = new byte[n];
		Iterator<Byte> it = lst.iterator();
		for(int i = 0; it.hasNext(); i++){
			array[i] = it.next();
		}
		if(next != null){
			array[n-1] = next;
		}
		return new Jump(array);
	}
	
	/*
	 * Searches through the space of possible "puts" to find the strongest for this move.
	 */
	private Move getBestPut(Board position, int depth){
		//TODO getBestPut method
		int bestStrength = Integer.MIN_VALUE;
		Put bestPut = null;
		/*
		for(int i = 0; i< 19;i++){
			for(int j = 0; j< 19; j++){
				if(position.on(i, j) == Constants.EMPTY){
					Field f = new Field(i,j);
					Put p = new Put(f);
					Board temp = (Board) position.clone();
					try {
						temp.move(p);
						int localStrength = adjuster(depth)*PhilBotCore.negaMax(depth, new WrapperPosition(temp, States.START), true);
						System.out.println(i + " " + j);
						if(localStrength > bestStrength){
							bestStrength = localStrength;
							bestPut = p;
							System.out.println("best put strength " + bestStrength);
						}
					} catch (InvalidMoveException e) {
						//do nothing
					}
				}
				
			}
		}
		*/
		return bestPut;
	}
	private int adjuster(int depth){
		return 1-2*(depth%2);
	}
}


